#include "virt0516h.h"


/*设置配置寄存器，用于进行各通道的温度转换，其中0x0001表示对通道0进行温度采集及转换；0x0002
表示对通道1进行温度采集及转换...*/
static uint16_t def_config_data[MAX_TEMP_NUM] = {0x0001, 0x0002, 0x003, 0x0004};

static int virt0516h_set_read_reg(struct i2c_client *client, uint8_t reg)
{
	unsigned char data;

	data = reg;
	printk("data=%d\n", data);
	return i2c_master_send(client, &data, sizeof(data));
}


static int virt0516h_i2c_read_data(struct i2c_client *client, int reg, void *dest, int bytes)
{
	int ret;

	if(NULL == dest)
	{
		printk(KERN_ERR"%s(%d):Input buffer is NULL\n", __FUNCTION__, __LINE__);
		return -EINVAL;
	}

	ret = virt0516h_set_read_reg(client, reg);
	if(ret != 0)
		return ret;


	return i2c_master_recv(client, dest, bytes);
}

static int virt0516h_set_config(struct i2c_client *client, uint16_t value)
{
    unsigned char buf[3];

    memset(buf, 0, sizeof(buf));

    buf[0] = VIRT0516H_CONFIG_REG;
    buf[1] = value>>8;
    buf[2] = value&0x00ff;

    return i2c_master_send(client, buf, sizeof(buf));
}



static int virt0516h_get_adc_code_by_index(struct i2c_client *client, int index, uint16_t *code)
{
	int ret = 0;
	uint8_t code_buff[2];


	if(index >= MAX_TEMP_NUM)
		return -EINVAL;

	if((ret = virt0516h_set_config(client, def_config_data[index])) != 0)
		return ret;

	memset(code_buff, 0, sizeof(code_buff));
	ret = virt0516h_i2c_read_data(client, VIRT0516H_CONVERSION_REG, code_buff, sizeof(code_buff));
	if(ret != 0)
		return ret;

	*code = code_buff[0]<<8|code_buff[1];

	return ret;
}

static int virt0516h_set_temp(struct i2c_client *client, uint8_t reg, uint16_t temp)
{
    unsigned char buf[3];

    memset(buf, 0, sizeof(buf));

    buf[0] = reg;
    buf[1] = temp>>8;
    buf[2] = temp&0x00ff;

    return i2c_master_send(client, buf, sizeof(buf));

}
static int virt0516h_get_temp(struct i2c_client *client, uint8_t reg, uint16_t *temp)
{
	int ret = 0;
	uint8_t code_buff[2];

	memset(code_buff, 0, sizeof(code_buff));
	ret = virt0516h_i2c_read_data(client, reg, code_buff, sizeof(code_buff));
	if(ret != 0)
		return ret;

	*temp = code_buff[0]<<8|code_buff[1];

	return ret;
}

static int virt0516h_update_client(struct device *dev)
{
	struct virt0516h_data_s *data = dev_get_drvdata(dev);
	struct i2c_client *client = data->client;

	mutex_lock(&data->update_lock);

	if (time_after(jiffies, data->last_updated) || !data->valid) {
		int i;

		for (i = 0; i < ARRAY_SIZE(data->temp_code); i++)
		{
			virt0516h_get_adc_code_by_index(client, i, &(data->temp_code[i]));
		}


		data->last_updated = jiffies;
		data->valid = 1;
	}

	mutex_unlock(&data->update_lock);
	
	return 0;
}



static const struct hwmon_channel_info *virt0516h_info[] = {
	HWMON_CHANNEL_INFO(temp,
			HWMON_T_INPUT | HWMON_T_MIN |HWMON_T_MAX |HWMON_I_ALARM|HWMON_I_MIN_ALARM|HWMON_I_MAX_ALARM,
			HWMON_T_INPUT | HWMON_T_MIN |HWMON_T_MAX |HWMON_I_ALARM|HWMON_I_MIN_ALARM|HWMON_I_MAX_ALARM,
			HWMON_T_INPUT | HWMON_T_MIN |HWMON_T_MAX |HWMON_I_ALARM|HWMON_I_MIN_ALARM|HWMON_I_MAX_ALARM,
			HWMON_T_INPUT | HWMON_T_MIN |HWMON_T_MAX |HWMON_I_ALARM|HWMON_I_MIN_ALARM|HWMON_I_MAX_ALARM),
	NULL
};
	
static umode_t virt0516_is_visible(const void *drvdata, enum hwmon_sensor_types type, u32 attr, int channel)
{
	switch(type)
	{
		case hwmon_temp:
			switch(attr)
				{
					case hwmon_temp_input:
					case hwmon_in_alarm:
						return S_IRUGO;
					case hwmon_temp_max:
					case hwmon_temp_min:
					case hwmon_in_min_alarm:
					case hwmon_in_max_alarm:
						return S_IRUGO | S_IWUSR;
					default:
						return (umode_t)0;
				}
			break;
		default:
			break;
	}

	return (umode_t)0;
}
static int virt0516_read(struct device *dev, enum hwmon_sensor_types type,
		u32 attr, int channel, long *val)
{
	struct virt0516h_data_s *data = dev_get_drvdata(dev);
	struct i2c_client *client = data->client;
	uint16_t temp = 0;	
	int ret = 0;
	
	if(type == hwmon_temp)
	{
		if(channel >= MAX_TEMP_NUM)
		{
			return -EINVAL;
		}

		switch(attr)
		{
			case hwmon_temp_input:
				virt0516h_update_client(dev);
				*val = data->temp_code[channel];
				return 0;
			case hwmon_temp_max:
				ret = virt0516h_get_temp(client, VIRT0516H_MAX_TEMP0_REG+channel, (uint16_t *)&temp);
				if(ret == 0)
					*val = temp;
				return ret;
			case hwmon_temp_min:
				ret = virt0516h_get_temp(client, VIRT0516H_MIN_TEMP0_REG+channel, (uint16_t *)&temp);
				if(ret == 0)
					*val = temp;
				return ret;
			case hwmon_temp_max_alarm:
				ret = virt0516h_get_temp(client, VIRT0516H_MAX_ALARM_TEMP0_REG+channel, (uint16_t *)&temp);
				if(ret == 0)
					*val = temp;
				return ret;
				return 0;
			case hwmon_temp_min_alarm:
				ret = virt0516h_get_temp(client, VIRT0516H_MIN_ALARM_TEMP0_REG+channel, (uint16_t *)&temp);
				if(ret == 0)
					*val = temp;
				return ret;
			default:
				return -EINVAL;
		}
	}

	return -EINVAL;
}

static int virt0516_write(struct device *dev, enum hwmon_sensor_types type,
		u32 attr, int channel, long val)
{
	struct virt0516h_data_s *data = dev_get_drvdata(dev);
	struct i2c_client *client = data->client;

	if(type == hwmon_temp)
	{
		if(channel >= MAX_TEMP_NUM)
		{
			return -EINVAL;
		}

		switch(attr)
		{
			case hwmon_temp_max:
				return virt0516h_set_temp(client, VIRT0516H_MAX_TEMP0_REG+channel, val);
			case hwmon_temp_min:
				return virt0516h_set_temp(client, VIRT0516H_MIN_TEMP0_REG+channel, val);
			case hwmon_temp_max_alarm:
				return virt0516h_set_temp(client, VIRT0516H_MAX_ALARM_TEMP0_REG+channel, val);
			case hwmon_temp_min_alarm:
				return virt0516h_set_temp(client, VIRT0516H_MIN_ALARM_TEMP0_REG+channel, val);
			default:
				return -EINVAL;
		}
	}

	return -EINVAL;
}

static const struct hwmon_ops virt0516_hwmon_ops = {
	.is_visible = virt0516_is_visible,
	.read = virt0516_read,
	.write = virt0516_write,
};

static const struct hwmon_chip_info virt0516_chip_info = {
	.ops = &virt0516_hwmon_ops,
	.info = virt0516h_info,
};


static int virt0516h_ctl_probe(struct i2c_client *client, const struct i2c_device_id *device_id)
{
	struct virt0516h_data_s *data;
	struct device *hwmon_dev;

	data = devm_kzalloc(&client->dev, sizeof(struct virt0516h_data_s),
		GFP_KERNEL);
	if(data == NULL)
		return -ENOMEM;

	data->client = client;
	mutex_init(&data->update_lock);
	hwmon_dev = devm_hwmon_device_register_with_info(&client->dev, client->name, data, &virt0516_chip_info, NULL);

	return PTR_ERR_OR_ZERO(hwmon_dev);
}

static int virt0516h_ctl_remove(struct i2c_client *client)
{

	printk("%s:%d\n", __FUNCTION__, __LINE__);
	return 0;
}

static const struct i2c_device_id virt0516h_ctl_id[] = 
{
	{ "vir0516h",0 },
	{ }
};
MODULE_DEVICE_TABLE(i2c, virt0516h_ctl_id);

#ifdef CONFIG_OF
static const struct of_device_id virt0516h_of_match[] = {
	{ .compatible = "vir, vir0516h" },
	{}
};
MODULE_DEVICE_TABLE(of, virt0516h_of_match);
#endif

static struct i2c_driver virt0516h_ctl_driver = {
	.driver = {
		.name	= "vir0516h",
		.owner	= THIS_MODULE,
		#ifdef CONFIG_OF
		.of_match_table = of_match_ptr(virt0516h_of_match),
		#endif
	},
	.probe		= virt0516h_ctl_probe,
	.remove = virt0516h_ctl_remove,
	.id_table	= virt0516h_ctl_id,
};


static int __init virt0516h_ctl_init(void)
{
	printk("%s:%d\n", __FUNCTION__, __LINE__);
	return i2c_add_driver(&virt0516h_ctl_driver);
}

static void __exit virt0516h_ctl_exit(void)
{
	i2c_del_driver(&virt0516h_ctl_driver);
}
module_init(virt0516h_ctl_init);
module_exit(virt0516h_ctl_exit);
MODULE_DESCRIPTION("vir0516h control Drivers"); 
MODULE_LICENSE("GPL");
